/**
 *
 */
package com.cmcc.pcap.packet.tcp;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.pcap4j.packet.TcpPacket;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * @Date 2018年1月17日 上午8:52:40
 * @author Amish
 * (1)存储一个tcp connection对应的所有Packet
 * (2)存储一个tcp connection的辅助信息，如tcp flags信息、最后消息时间等。可用于辅助判断connection状态。
 * (3)提供HTTP事务划分方法
 * (4)提供tcp segment重组的方法
 * (5)提供基于这个TcpConnection的各种统计：包数量、流量、乱序数量、ACK SYN等数量
 */

/**
 * (1)TCP Segment Reassemble (TCP分段重组):
 * (2)TODO TOFIX TCP Out-Of-Order(TCP乱序):应该放到HTTP事务识别之后？
 * (3)TOFIX TCP Previous segment not captured:  do nothing
 * (4)TCP Dup ACK(ACK重复应答) :Sequence number相同，acknowledgement number也相同的，且“只有”ACK置位，data Length=0，为重复的ACK（注，一个大TCP包的所有分段的对应的ACK的Sequence number相同），TODO 是否需要清洗待定
 * (5)TCP Retransmission(TCP重传):Sequence number相同，acknowledgement number也相同的,data Length!=0
 * (6)TCP Spurious Retransmission(TCP虚假重传,rfc4138):Sequence number相同，acknowlegdement number也相同的,data Length!=0
 */
public class TcpConnection {
    public static Logger logger = LogManager.getLogger(TcpConnection.class);

    public String connectionKey;//标识一个Connection的唯一Key，形如  “IP:PORT-IP:PORT”  //在处理Connection的第一个SYN消息时设置
    public String clientAddr;//TCP连接的客户端，形如 “IP:PORT”//在处理Connection的第一个SYN消息时设置
    public String serverAddr;//TCP连接的服务端，形如 “IP:PORT”//在处理Connection的第一个SYN消息时设置

    public TcpProtocol protocol = TcpProtocol.TCP;//TCP链接上对应的应用层协议，如HTTP等。默认就是TCP，包括无法识别应用层协议的分片以及无data的包，此处都是TCP。
    public TcpPacketDecorator firstDataTcpPacketDecorator;//第一个带Tcp Payload/data的包 //在向cache中加入该包且filter过滤后设置
    public ArrayList<TcpPacketDecorator> al;//保存该连接初始收到的所有TcpPacket，TODO ArrayList or ConnectionHashMap?  TODO reassemble后是否需要清空？

    public boolean haveRST = false; //有一条RST packet，就置为true
    public boolean haveFin = false; //有一条FIN packet，就置为true
    public long lastPacketTime;//connection最后一个packet的加入cache的时间(注:非抓包时间)

    /**
     * 三次握手对应的包
     */
    public TcpPacket handshakeTcpPacket_syn;
    public TcpPacket handshakeTcpPacket_syn_ack;
    public TcpPacket handshakeTcpPacket_ack;
    /**
     * 四次挥手对应的包
     */
    public TcpPacket waveTcpPacket_clientsend_fin;
    public TcpPacket waveTcpPacket_clientreceive_ack;
    public TcpPacket waveTcpPacket_serversend_fin;
    public TcpPacket waveTcpPacket_serverreceive_ack;
    /**
     * RST对应的包
     */
    public TcpPacket tcpPacket_rst;

    /**
     * 此三个list依次依赖前一个list处理，而duplicateAckMergeAl依赖最开始的al
     */
    public ArrayList<TcpPacketDecorator> duplicateAckMergeAl;//保存Duplicate ACK merge后的所有TcpPacketDecorator
    public ArrayList<TcpPacketDecorator> retransmissionMergeAl;//保存Retransmission/Spurious Retransmission merge后的所有TcpPacketDecorator
    public ArrayList<TcpPacketDecorator> reassembleAl;//保存Segment Reassemble后的所有TcpPacketDecorator
    public ArrayList<TcpPacketDecorator> reorderTcpPacketAl;//保存tcp包乱序重组后的所有TcpPacketDecorator

    /**
     * 部分针对该Connection的统计数据
     */
    public int packetsNumInitial;//初始包数
    public int packetsNumAfterDuplicateAckMerge;//Duplicate ACK merge后的包数
    public int packetsNumAfterRetransmissionMerge;//Retransmission/Spurious Retransmission merge后包数
    public int packetsNumAfterReorderTcpPacket;//ReorderTcpPacket后的包数
    public int packetsNumAfterReassemble;//Segment Reassemble后的包数

    public int OutOfOrderNum = 0;
    public boolean retransmissionMerge; //是否重传

    public TcpConnection() {
        al = new ArrayList<TcpPacketDecorator>();
    }


    /**
     * 需要依次调用：
     * TODO this.duplicateAckMerge();和this.retransmissionMerge();如果合并，其实可以通过一次遍历完成，可优化性能。
     * TODO 四个ArrayList如果依次删除其实可以节省内存
     */
    public void tcpConnectionPacketsDeal() {
        //TODO TCP Out-Of-Order(TCP乱序)

        this.duplicateAckMerge();
        this.packetsNumInitial = al.size();
        al.clear();

        this.retransmissionMerge();
        this.packetsNumAfterDuplicateAckMerge = duplicateAckMergeAl.size();
        duplicateAckMergeAl.clear();


        this.reOrderTcpPacket();
        this.packetsNumAfterRetransmissionMerge = retransmissionMergeAl.size();
        retransmissionMergeAl.clear();

        this.reassembleTcpPackets();
        this.packetsNumAfterReorderTcpPacket = reorderTcpPacketAl.size();
        reorderTcpPacketAl.clear();

//    	this.reassembleTcpPackets();
//		this.packetsNumAfterRetransmissionMerge = retransmissionMergeAl.size();
//		retransmissionMergeAl.clear();

        packetsNumAfterReassemble = reassembleAl.size();//记录分段重组后的包数
    }

    /**
     * (2)TCP Out-Of-Order(TCP乱序)
     */
    public void reOrderPacket(int init, int size) {
        int p = 0;//p用于标记乱序的数据包
        int unorder = 0;
        int j = init + 1;//j为当前包的下一个同方向包的位置
        while (j < size) {
            String i_ip = reorderTcpPacketAl.get(init).ipPacket.getHeader().getSrcAddr().toString();
            String j_ip = reorderTcpPacketAl.get(j).ipPacket.getHeader().getSrcAddr().toString();
            if (!j_ip.equals(i_ip))
                ++j;
            else
                break;
        }
        while (j < size) {

            //计算IP包头长度
            long ip_header_length = reorderTcpPacketAl.get(init).ipPacket.getHeader().length();

            //计算TCP包头长度
            long tcp_header_length = reorderTcpPacketAl.get(init).ipPacket.get(TcpPacket.class).getHeader().length();

            //IP包头和TCP包头长度之和
            long total_header_length = ip_header_length + tcp_header_length;

//			long j_sequence = reorderTcpPacketAl.get(j).tcpPacket.sequence;
//			long i_sequence = reorderTcpPacketAl.get(init).tcpPacket.sequence;
//			String i_ip = reorderTcpPacketAl.get(init).tcpPacket.src_ip.toString();
//			String j_ip = reorderTcpPacketAl.get(j).tcpPacket.src_ip.toString();
//			long i_length = reorderTcpPacketAl.get(init).tcpPacket.length-total_header_length;

            if ((reorderTcpPacketAl.get(j).ipPacket.get(TcpPacket.class).getHeader().getFin()) && (reorderTcpPacketAl.get(init).ipPacket.get(TcpPacket.class).getHeader().getSequenceNumberAsLong() + reorderTcpPacketAl.get(init).ipPacket.get(TcpPacket.class).length() - tcp_header_length == reorderTcpPacketAl.get(j).ipPacket.get(TcpPacket.class).getHeader().getSequenceNumberAsLong())) {
                //logger.info("执行了fin" + "sequence:" + " " + reorderTcpPacketAl.get(j).tcpPacket.sequence);
                break;
            } else {
                if (reorderTcpPacketAl.get(init).ipPacket.get(TcpPacket.class).getHeader().getSequenceNumberAsLong() + reorderTcpPacketAl.get(init).ipPacket.get(TcpPacket.class).length() - tcp_header_length == reorderTcpPacketAl.get(j).ipPacket.get(TcpPacket.class).getHeader().getSequenceNumberAsLong()) {
                    init = j;
                    j = init + 1;
                    while (j < size) {
                        if (!reorderTcpPacketAl.get(j).ipPacket.getHeader().getSrcAddr().toString().equals(reorderTcpPacketAl.get(init).ipPacket.getHeader().getSrcAddr().toString()))
                            ++j;
                        else
                            break;
                    }
                } else {//当序号不连续时，会有三种情况出现。1，数据包确实有乱序；2，数据包丢包了；3，遇到了序号错乱的数据包，其实在序号错乱的数据包后面有 满足顺序序号的数据包，只是被序号错乱的数据包打断了
                    p = j;
                    //logger.info("修改了p值" + reorderTcpPacketAl.get(p).tcpPacket.sequence);
                    while (j < size) {
                        if ((reorderTcpPacketAl.get(init).tcpPacket.getHeader().getSequenceNumberAsLong() + reorderTcpPacketAl.get(init).tcpPacket.length() - tcp_header_length != reorderTcpPacketAl.get(j).tcpPacket.getHeader().getSequenceNumberAsLong()) && (reorderTcpPacketAl.get(j).ipPacket.getHeader().getSrcAddr().toString().equals(reorderTcpPacketAl.get(init).ipPacket.getHeader().getSrcAddr().toString()))) {
                            if (reorderTcpPacketAl.get(init).tcpPacket.getHeader().getSequenceNumberAsLong() + reorderTcpPacketAl.get(init).tcpPacket.length() - tcp_header_length < reorderTcpPacketAl.get(j).tcpPacket.getHeader().getSequenceNumberAsLong())
                                ++j;
                            else {//遇到了乱序数据包，标记乱序，但是不交换
                                if ((reorderTcpPacketAl.get(j).tcpPacket.getHeader().getAck() && reorderTcpPacketAl.get(j).tcpPacket.length() - tcp_header_length == 0)) {
                                    logger.debug("ack num:" + reorderTcpPacketAl.get(j).tcpPacket.getHeader().getAck() + " " + "length num:" + reorderTcpPacketAl.get(j).tcpPacket.length() + ";" + reorderTcpPacketAl.get(j).tcpPacket.length() + ";" + tcp_header_length);
                                    logger.debug("len:" + reorderTcpPacketAl.get(j).tcpPacket.length());
                                    ++j;
                                } else {
                                    logger.debug("遇到了乱序数据包:" + reorderTcpPacketAl.get(j).tcpPacket.getHeader().getSequenceNumberAsLong());
                                    unorder = 1;
                                    reorderTcpPacketAl.get(j).isOutOfOrder = true;
                                    synchronized (this) {
                                        ++OutOfOrderNum;
                                    }
                                    ++j;
                                }
                            }
                        }//如果一直往后找，还是没有找到相应的数据包，则最后j的值会变为一个停车票connection中数据包的总长度，进而跳出循环
                        else if ((reorderTcpPacketAl.get(init).tcpPacket.getHeader().getSequenceNumberAsLong() + reorderTcpPacketAl.get(init).tcpPacket.length() - tcp_header_length != reorderTcpPacketAl.get(j).tcpPacket.getHeader().getSequenceNumberAsLong()) && (!reorderTcpPacketAl.get(j).ipPacket.getHeader().getSrcAddr().toString().equals(reorderTcpPacketAl.get(init).ipPacket.getHeader().getSrcAddr().toString()))) {
                            ++j;
                        }//向后寻找同一数据方向的数据包的时候，需要确认数据的方向，否则会出现bug
                        else {
                            if (unorder == 0) {
                                reorderTcpPacketAl.get(j).isOutOfOrder = true;
                                synchronized (this) {
                                    ++OutOfOrderNum;
                                }

                            }
                            break;
                        }
                    }

                    //将此刻找到的j位置的数据包插入p位置前面
                    if (j < size && unorder == 0) {//此处处理未丢包的情况
                        //logger.info("j 的值"+j);
                        //logger.info("执行了交换");
//						long p_temp_sequence = reorderTcpPacketAl.get(p).tcpPacket.sequence;
//						long j_temp_sequence = reorderTcpPacketAl.get(j).tcpPacket.sequence;

                        TcpPacketDecorator temp = reorderTcpPacketAl.get(j);
                        reorderTcpPacketAl.remove(j);
                        reorderTcpPacketAl.add(p, temp);

//						long p_temp_sequence1 = reorderTcpPacketAl.get(p).tcpPacket.sequence;
//						long j_temp_sequence1 = reorderTcpPacketAl.get(j).tcpPacket.sequence;

                        j = p;
//						long j_temp_sequence2 = reorderTcpPacketAl.get(j).tcpPacket.sequence;
                    }
                    if (j == size && unorder == 0) {//此处解决出现丢包的问题。有两种情况，一种是丢包后的后续数据包没有乱序；另一种是丢包后的后续数据包存在乱序
                        //logger.info("出现了丢包");
                        j = p + 1;
                        while (j < size) {
                            if (reorderTcpPacketAl.get(j).tcpPacket.getHeader().getSequenceNumberAsLong() < reorderTcpPacketAl.get(p).tcpPacket.getHeader().getSequenceNumberAsLong()) {
                                reorderTcpPacketAl.get(j).isOutOfOrder = true;
                                ++OutOfOrderNum;
                                TcpPacketDecorator temp = reorderTcpPacketAl.get(j);
                                reorderTcpPacketAl.remove(j);
                                reorderTcpPacketAl.add(p, temp);
                                j = p + 1;
                                init = p;
                            } else
                                ++j;
                        }
                        if (j == size) {
                            init = p;
                            j = p + 1;
                        }

                    }
                    if (unorder == 1) {
                        //遇到一个序号错乱的包只对它进行标记，其他什么都不做，所以在下次循环之前必须将标记的值变为初始值
                        unorder = 0;
                    }

                }
            }

        }
//		logger.error("=========================================================");
//		logger.error("out of order number"+OutOfOrderNum);
//		logger.error("=========================================================");
    }


    public void reOrderTcpPacket() {
        reorderTcpPacketAl = new ArrayList<TcpPacketDecorator>();
        Iterator<TcpPacketDecorator> intertor = retransmissionMergeAl.iterator();
        while (intertor.hasNext()) {
            reorderTcpPacketAl.add(intertor.next());
        }

        //从第四个包开始,向后找到第一个与src_num位置IP不同的IP，记其位置为dst_num,则src_num与dst_num分别是client和server
        int src_num = 3;
        int dst_num = src_num + 1;
        int size = reorderTcpPacketAl.size();

        while (dst_num < size) {
            if (reorderTcpPacketAl.get(src_num).ipPacket.getHeader().getSrcAddr().toString().equals(reorderTcpPacketAl.get(dst_num).ipPacket.getHeader().getSrcAddr().toString()))
                ++dst_num;
            else
                break;
        }

        if (src_num != size)
            this.reOrderPacket(src_num, size);

        if (dst_num != size)
            this.reOrderPacket(dst_num, size);

        logger.debug("out of order packets num : " + OutOfOrderNum);
    }


    //此方法仅用于识别协议为tcp的tcpconnection使用，用于包的乱序重排
    public void reOrderTcpPacket2() {
        reorderTcpPacketAl = new ArrayList<TcpPacketDecorator>();
        Iterator<TcpPacketDecorator> intertor = al.iterator();
        while (intertor.hasNext()) {
            reorderTcpPacketAl.add(intertor.next());
        }

        //从第四个包开始,向后找到第一个与src_num位置IP不同的IP，记其位置为dst_num,则src_num与dst_num分别是client和server
        int src_num = 3;
        int dst_num = src_num + 1;
        int size = reorderTcpPacketAl.size();

        while (dst_num < size) {
            if (reorderTcpPacketAl.get(src_num).ipPacket.getHeader().getSrcAddr().toString().equals(reorderTcpPacketAl.get(dst_num).ipPacket.getHeader().getSrcAddr().toString()))
                ++dst_num;
            else
                break;
        }

        if (src_num != size)
            this.reOrderPacket(src_num, size);

        if (dst_num != size)
            this.reOrderPacket(dst_num, size);

        logger.debug("out of order packets num : " + OutOfOrderNum);
    }


    /**
     * (4) TCP Dup ACK(ACK重复应答)
     */
    public void duplicateAckMerge() {
        duplicateAckMergeAl = new ArrayList<TcpPacketDecorator>();


        Iterator<TcpPacketDecorator> intertor = al.iterator();
        while (intertor.hasNext()) {
            TcpPacketDecorator tcpPacketDecorator = intertor.next();

            if (tcpPacketDecorator.tcpPacket.getPayload() == null) {

                int check = this.checkSeqNumAndAckNum(duplicateAckMergeAl, tcpPacketDecorator);
                if (check != -1) {
                    TcpPacketDecorator tpd = duplicateAckMergeAl.get(check);
                    tpd.isDuplicateAckMerge = true;
                    tpd.duplicateAckMergeAl = new ArrayList<TcpPacketDecorator>();
                    tpd.duplicateAckMergeAl.add(tcpPacketDecorator);
                } else {
                    duplicateAckMergeAl.add(tcpPacketDecorator);
                }
            } else {
                duplicateAckMergeAl.add(tcpPacketDecorator);
            }
        }


//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//		logger.error(duplicateAckMergeAl.size());
//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

    }

    /**
     * (5) TCP Retransmission(TCP重传)/(6) TCP Spurious Retransmission(TCP虚假重传,rfc4138)
     */
    public void retransmissionMerge() {
        retransmissionMergeAl = new ArrayList<TcpPacketDecorator>();

        Iterator<TcpPacketDecorator> intertor = duplicateAckMergeAl.iterator();
        while (intertor.hasNext()) {
            TcpPacketDecorator tcpPacketDecorator = intertor.next();

            if (tcpPacketDecorator.tcpPacket.getPayload() != null) {

                int check = this.checkSeqNumAndAckNum(retransmissionMergeAl, tcpPacketDecorator);
                if (check != -1) {
                    TcpPacketDecorator tpd = retransmissionMergeAl.get(check);
                    tpd.isRetransmissionMerge = true;
                    this.retransmissionMerge = true;
                    tpd.retransmissionMergeAl = new ArrayList<TcpPacketDecorator>();
                    tpd.retransmissionMergeAl.add(tcpPacketDecorator);
                } else {
                    retransmissionMergeAl.add(tcpPacketDecorator);
                }
            } else {
                retransmissionMergeAl.add(tcpPacketDecorator);
            }
        }


//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//		logger.error(retransmissionMergeAl.size());
//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    }

    /**
     * (1) TCP Segment Reassemble (TCP分段重组)
     * PS:所有的Acknowledgement Num和Sequence number应该通过一个ISN(Inital Sequence Number),得到一个relative sequence number 和 relative ack number，方便查看和计算，模仿wireshark
     * <p>
     * 分段重组逻辑：
     * 1、data length!=0才有可能是分段
     * 2、PSH==1,可能是   a非分段的消息；c分段的中间某个；d分段的最后一个；
     * 3、PSH!=1,可能是   b分段的第一个；c分段的中间某个；
     * 4、一个包的不同分段Sequence number是由小到大连续的(当然会有乱序的情况)；
     * 5、一个包的不同分段Acknowledgment是相同的(都是对之前对向包的数据长度+Sequence number)；
     */
    public void reassembleTcpPackets() {

        reassembleAl = new ArrayList<TcpPacketDecorator>();//重组后的包List

        Iterator<TcpPacketDecorator> intertor = reorderTcpPacketAl.iterator();
//		Iterator<TcpPacketDecorator> intertor = retransmissionMergeAl.iterator();
        while (intertor.hasNext()) {
            TcpPacketDecorator tcpPacketDecorator = intertor.next();

            //this.testOnly(tcpPacketDecorator);//Test Only

            if (tcpPacketDecorator.tcpPacket.getPayload() != null) {

                int check = this.checkAckNum(reassembleAl, tcpPacketDecorator);
                if (check != -1) {
                    TcpPacketDecorator tpd = reassembleAl.get(check);
                    if (tpd.isReassemble == true) {
                        tpd.segmentsAl.add(tcpPacketDecorator);
                    } else {
                        tpd.isReassemble = true;
                        tpd.segmentsAl = new ArrayList<TcpPacketDecorator>();
                        tpd.segmentsAl.add(tcpPacketDecorator);
                    }

                } else {
                    reassembleAl.add(tcpPacketDecorator);
                }
            } else {
                reassembleAl.add(tcpPacketDecorator);
            }
        }

//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//		logger.error(reassembleAl.size());
//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");


    }

    /**
     * 用于判断TCP 分段的 Ack Num,同时需要检查是否有data、数据方向是否相同
     *
     * @param
     * @param tpd
     * @return
     */
    private int checkAckNum(ArrayList<TcpPacketDecorator> argAl, TcpPacketDecorator tpd) {
        for (TcpPacketDecorator saveTpd : argAl) {
            if (saveTpd.tcpPacket.getPayload() != null
                    && saveTpd.tcpPacket.getPayload().length() > 0
                    && saveTpd.tcpPacket.getHeader().getAcknowledgmentNumberAsLong() == tpd.tcpPacket.getHeader().getAcknowledgmentNumberAsLong()
                    && saveTpd.ipPacket.getHeader().getSrcAddr().getHostAddress().equalsIgnoreCase(tpd.ipPacket.getHeader().getSrcAddr().getHostAddress())
                    && saveTpd.tcpPacket.getHeader().getSrcPort().value().equals(tpd.tcpPacket.getHeader().getSrcPort().value())) {
                return argAl.indexOf(saveTpd);
            }
        }
        return -1;
    }

    /**
     * 用于判断Duplicate ACK/以及 TCP Spurious Retransmission/TCP Retransmission的情况，同时需要检查数据方向
     * PS 注意，最后一个无数据的ACK和(RST+ACK)或(FIN+ACK)的Ack Num和Seq Num可能相同
     *
     * @param
     * @param tpd
     * @return
     */
    private int checkSeqNumAndAckNum(ArrayList<TcpPacketDecorator> argAl, TcpPacketDecorator tpd) {
        for (TcpPacketDecorator saveTpd : argAl) {
            if (saveTpd.tcpPacket.getPayload() != null&&tpd.tcpPacket.getPayload()!=null) {
                if (this.checkFlags(saveTpd, tpd)
                        && saveTpd.tcpPacket.getHeader().getAcknowledgmentNumberAsLong() == tpd.tcpPacket.getHeader().getAcknowledgmentNumberAsLong()
                        && saveTpd.tcpPacket.getHeader().getSequenceNumberAsLong() == tpd.tcpPacket.getHeader().getSequenceNumberAsLong()
                        && saveTpd.ipPacket.getHeader().getSrcAddr().getHostAddress().equalsIgnoreCase(tpd.ipPacket.getHeader().getSrcAddr().getHostAddress())
                        && saveTpd.tcpPacket.getHeader().getSrcPort().value().equals(tpd.tcpPacket.getHeader().getSrcPort().value())
                        && saveTpd.tcpPacket.getPayload().length() == tpd.tcpPacket.getPayload().length()) {
                    return argAl.indexOf(saveTpd);
                }
            }
        }
        return -1;
    }

    /**
     * 检查两个包的tcp flags是否完全相同
     *
     * @param saveTpd
     * @param tpd
     * @return
     */
    private boolean checkFlags(TcpPacketDecorator saveTpd, TcpPacketDecorator tpd) {
        return (saveTpd.tcpPacket.getHeader().getSyn() == tpd.tcpPacket.getHeader().getSyn())
                && ((saveTpd.tcpPacket.getHeader().getAck() == tpd.tcpPacket.getHeader().getAck()))
                && ((saveTpd.tcpPacket.getHeader().getRst() == tpd.tcpPacket.getHeader().getRst()))
                && ((saveTpd.tcpPacket.getHeader().getFin() == tpd.tcpPacket.getHeader().getFin()));
    }


    public int downPakcetNum() {//TODO TODO TODO TODO TODO
        return 0;
    }

    public int upPacketNum() {//TODO TODO TODO TODO TODO
        return 0;
    }

    public int downFlow() {//TODO TODO TODO TODO TODO
        return 0;
    }

    public int upFlow() {//TODO TODO TODO TODO TODO
        return 0;
    }

}
